﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;

namespace AutoTestEntry.Utility
{
    public class FtpDownloader
    {  
        private FtpWebRequest reqFTP;
        private FileTree treeRoot;
        private int downloadedSize;
        private String type;

        #region DownloadEvent
        public class DownloadEventArgs : EventArgs
        {
            public readonly int FinishedPercent;
            public readonly String Type;
            public DownloadEventArgs(int finishedPercent, String type)
            {
                FinishedPercent = finishedPercent;
                Type = type;
            }
        }
        //委托
        public delegate void DownloadEventHandler(Object sender, DownloadEventArgs e);

        //事件
        public event DownloadEventHandler DownloadEvent;

        private void RaiseDownloadEvent(int count)
        {
            downloadedSize += count;
			DownloadEvent(this, new DownloadEventArgs((int)(((float)downloadedSize / treeRoot.Size) * 100), type));  //防止溢出
        }
        #endregion
        
        #region BaseFtpFunctions
        private bool Connect(String url)
        {
            try
            {
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
                reqFTP.UseBinary = true;
                return true;
            }
            catch
            {
                return false;
            }
        }

        private String[] GetFileList(String path)
        {
            StringBuilder result = new StringBuilder();

            try
            {
                if (!Connect(path))
                {
                    return null;
                }
                
                reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                WebResponse response = reqFTP.GetResponse();

                // 设置语言
                StreamReader reader = new StreamReader(response.GetResponseStream(),
                    System.Text.Encoding.Default);

                String line = reader.ReadLine();
                while (line != null)
                {
                    result.Append(line);
                    result.Append("\n");
                    line = reader.ReadLine();
                }

                result.Remove(result.ToString().LastIndexOf('\n'), 1);
                reader.Close();
                response.Close();

                return result.ToString().Split('\n');
            }
            catch
            {
                return null;
            }
        }

        public String[] GetBriefFileList(String path)
        {
            StringBuilder result = new StringBuilder();

            try
            {
                Connect(path);
                reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
                WebResponse response = reqFTP.GetResponse();

                // 设置语言
                StreamReader reader = new StreamReader(response.GetResponseStream(),
                    System.Text.Encoding.Default);

                String line = reader.ReadLine();
                while (line != null)
                {
                    result.Append(line);
                    result.Append("\n");
                    line = reader.ReadLine();
                }

                result.Remove(result.ToString().LastIndexOf('\n'), 1);
                reader.Close();
                response.Close();

                return result.ToString().Split('\n');
            }
            catch
            {
                return null;
            }
        }
        #endregion

        #region BuildFileTree
        private void BuildFileTree(String ftpPath, bool isFile)
        {
            String[] files = GetFileList(ftpPath);

            if (isFile)
            {
				if (files == null)
				{
					treeRoot = null;
					return;
				}
				else
				{
					treeRoot = new FileTree(FileTree.TYPES.TYPE_FILE, ftpPath);
					String[] temp = files[0].Trim().Split(new String[] { " " }, StringSplitOptions.RemoveEmptyEntries);
					treeRoot.Size = Convert.ToInt32(temp[2].Trim());
					return;
				}
            } 
            else
            {
                treeRoot = new FileTree(FileTree.TYPES.TYPE_DIR, ftpPath);
				if (files == null)
					return;

                for (int i = 0; i < files.Length; ++i )
                {
                    String[] temp = null;
                    FileTree child;
                    if (files[i].Contains("<DIR>"))
                    {
                        temp = files[i].Trim().Split(new String[] { "<DIR>" }, StringSplitOptions.None);
                        child = BuildSubFileTree(ftpPath, temp[1].Trim());
                    }
                    else
                    {
                        temp = files[i].Trim().Split(new String[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                        child = new FileTree(FileTree.TYPES.TYPE_FILE, temp[3].Trim());
                        child.Size = Convert.ToInt32(temp[2].Trim());
                    }
					if (child != null)
					{
						treeRoot.AddChild(child);
						treeRoot.Size += child.Size;
					}
                }
            }
        }

        private FileTree BuildSubFileTree(String upperPath, String curPath)
        {
            FileTree curNode = new FileTree(FileTree.TYPES.TYPE_DIR, curPath);
            String[] files = GetFileList(upperPath + "/" +curPath);
			if (files == null)
			{
				return curNode;
			}

            for (int i = 0; i < files.Length; ++i)
            {
                String[] temp = null;
                FileTree child;
                if (files[i].Contains("<DIR>"))
                {
                    temp = files[i].Trim().Split(new String[] { "<DIR>" }, StringSplitOptions.None);
                    child = BuildSubFileTree(upperPath + "/" + curPath, temp[1].Trim());
                }
                else
                {
                    temp = files[i].Trim().Split(new String[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                    child = new FileTree(FileTree.TYPES.TYPE_FILE, temp[3].Trim());
                    child.Size = Convert.ToInt32(temp[2].Trim());
                }
                curNode.AddChild(child);
                curNode.Size += child.Size;
            }
            return curNode;
        }
        #endregion

        #region Download
        public bool Download(String ftpPath, String localPath, bool isFile, String type)
        {
            this.type = type;
            downloadedSize = 0;
            BuildFileTree(ftpPath, isFile);
			if (treeRoot == null)
			{
				return false;
			}

            String lastPath = Path.GetFileName(ftpPath);
            if (lastPath == "" && ftpPath[ftpPath.Length - 1] == '/')
            {
                lastPath = Path.GetFileName(ftpPath.Substring(0, ftpPath.Length - 1));
            }

            if (treeRoot.Type == FileTree.TYPES.TYPE_FILE)
            {
                DownloadSinleFlile(treeRoot.Path, localPath + "/" + lastPath);
            }
            else
            {
                DownloadDir(treeRoot, treeRoot.Path, localPath + "\\" + lastPath);
            }
            return true;
        }

        private bool DownloadSinleFlile(String url, String fileName)
        {
            //如果文件已经存在，则不重新下载
            //if (File.Exists(fileName))
            //{
            //    return false;
            //}
            try
            {
                Connect(url);

                reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;

                WebResponse response = reqFTP.GetResponse();
                Stream ftpStream = response.GetResponseStream();
                long cl = response.ContentLength;
                int bufferSize = 2048;
                int readCount;
                byte[] buffer = new byte[bufferSize];
                readCount = ftpStream.Read(buffer, 0, bufferSize);
                FileStream outputStream = new FileStream(fileName, FileMode.Create);
                while (readCount > 0)
                {
                    outputStream.Write(buffer, 0, readCount);
                    outputStream.Flush();
                    RaiseDownloadEvent(readCount);
                    readCount = ftpStream.Read(buffer, 0, bufferSize);
                }

                ftpStream.Close();
                outputStream.Close();
                response.Close();

                return true;
            }
            catch
            {
                return false;
            }
        }

        private void DownloadDir(FileTree fileNode, String ftpPath, String localPath)
        {
            if (!Directory.Exists(localPath))
            {
                Directory.CreateDirectory(localPath);
            }

            FileTree[] children = fileNode.GetAllChildren();
            for (int i = 0; i < children.Length; ++i )
            {
                if (children[i].Type == FileTree.TYPES.TYPE_FILE)
                {
                    DownloadSinleFlile(ftpPath + "/" + children[i].Path, localPath + "\\" + children[i].Path);
                } 
                else
                {
                    DownloadDir(children[i], ftpPath + "/" + children[i].Path, localPath + "\\" + children[i].Path);
                }
            }
        }
        #endregion
    }
}